#coding=utf-8
import random
from PriorityQueue import PriorityQueue as pq
from task import Task
from job import Job
from file_path import *
'''
f = open('./result/3_0.6/test3/simu_ddl.log', 'w')
'''
# path = file_path() + 'ins_act.log'
# f = open(path,'w')

def Ini_joblist(task_set,until,rt,file_name):
    path = file_name + 'ins_act.log'
    f = open(path,'w')
    joblist = []
    for i in range(len(task_set)):
        t_release = i*rt
        k = 1
        t_upload = task_set[i].getUpcost()
        t_compute = task_set[i].getComcost()
        t_mddl = task_set[i].getM_DDL() + t_release
        t_ddl = task_set[i].getDDL() + t_release
        t_period = task_set[i].getPeriod()
        j = Job(t_release,t_upload,t_compute,t_mddl,t_ddl,t_period,'%d_%d'%(i+1,k) )
        while(t_ddl <= until):
            joblist.append(j)
            t_release += t_period
            t_mddl += t_period
            t_ddl += t_period
            k += 1
            j = Job(t_release,t_upload,t_compute,t_mddl,t_ddl,t_period,'%d_%d'%(i+1,k) )
    return joblist

def Ini_jobpq(joblist):
    jobpq = pq()
    for i in range(len(joblist)):
        jobpq.push(joblist[i],joblist[i].getRelease_time())
    return jobpq

def Ini_channelpq(jobpq,Pmax):
    channel_pq = pq()
    if(jobpq.len() == 2):
        j1 = jobpq.pop()
        pri1 = j1.getRelease_time()
        j2 = jobpq.pop()
        pri2 = j2.getRelease_time()
        if(pri1 == pri2):
            channel_pq.push(j1,SRPTprio(j1,Pmax))
            channel_pq.push(j2,SRPTprio(j2,Pmax))
        else:
            channel_pq.push(j1,SRPTprio(j1,Pmax))
            jobpq.push(j2,j2.getRelease_time())
        return channel_pq
    if(jobpq.len()>=3):
        j1 = jobpq.pop()
        pri1 = j1.getRelease_time()
        channel_pq.push(j1,SRPTprio(j1,Pmax))
        j2 = jobpq.pop()
        pri2 = j2.getRelease_time()
        flag = 1
        while(pri2 == pri1):
            flag = 0
            channel_pq.push(j2,SRPTprio(j2,Pmax))
            if jobpq.len() >= 1:
                j2 = jobpq.pop()
                pri2 = j2.getRelease_time()
                if pri1 != pri2:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq.push(j2,j2.getRelease_time())
        return channel_pq

def Add_channelEDF(jobpq1,channelpq,Pmax):
    if(jobpq1.len() == 0):
        return channelpq
    elif(jobpq1.len() == 1):
        j1 = jobpq1.pop()
        channelpq.push(j1,EDFprio(j1,Pmax))
    elif(jobpq1.len() == 2):
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channelpq.push(j1,EDFprio(j1,Pmax))
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        if pri2 == pri1:
            channelpq.push(j2,EDFprio(j2,Pmax))
        else:
            jobpq1.push(j2,j2.getRelease_time())
    else:
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channelpq.push(j1,EDFprio(j1,Pmax))
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        flag = 1
        while(pri2 == pri1):
            flag = 0
            channelpq.push(j2,EDFprio(j2,Pmax))
            if(jobpq1.len() >= 1):
                j2 = jobpq1.pop()
                pri2 = j2.getRelease_time()
                if pri2 != pri1:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq1.push(j2,j2.getRelease_time())
    return channelpq

def Add_channelSRPT(jobpq1,channelpq,Pmax):
    if(jobpq1.len() == 0):
        return channelpq
    elif(jobpq1.len() == 1):
        j1 = jobpq1.pop()
        channelpq.push(j1,SRPTprio(j1,Pmax))
    elif(jobpq1.len() == 2):
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channelpq.push(j1,SRPTprio(j1,Pmax))
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        if pri2 == pri1:
            channelpq.push(j2,SRPTprio(j2,Pmax))
        else:
            jobpq1.push(j2,j2.getRelease_time())
    else:
        j1 = jobpq1.pop()
        pri1 = j1.getRelease_time()
        channelpq.push(j1,SRPTprio(j1,Pmax))
        j2 = jobpq1.pop()
        pri2 = j2.getRelease_time()
        flag = 1
        while(pri2 == pri1):
            flag = 0
            channelpq.push(j2,SRPTprio(j2,Pmax))
            if(jobpq1.len() >= 1):
                j2 = jobpq1.pop()
                pri2 = j2.getRelease_time()
                if pri2 != pri1:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq1.push(j2,j2.getRelease_time())
    return channelpq

def SwitchPri(channelpq,flag,Pmax):
    new_pq = pq()
    n = channelpq.len()
    if flag == 1:
        for i in range(n):
            job = channelpq.pop()
            new_pq.push(job,EDFprio(job,Pmax))
    else:
        for i in range(n):
            job = channelpq.pop()
            new_pq.push(job,SRPTprio(job,Pmax))
    return new_pq

def GetMaxP(task_set,file_name):
    path = file_name + 'ins_act.log'
    f = open(path,'a')
    Pmax = 0
    for i in range(len(task_set)):
        P = task_set[i].getPeriod()
        if P >= Pmax:
            Pmax = P
    print("pmax is: ",Pmax,file=f)
    return Pmax+1

def SRPTprio(job,Pmax):
    # prio = remaining upload time * p * p + deadline * p - computing time
    prio = job.rest_rtime*0.001 + job.getM_DDL()
    # prio = job.rest_rtime*1000 + job.getDDL()*1
    return prio

def EDFprio(job,Pmax):
    # prio = remaining upload time * p * p + deadline * p - computing time
    prio = job.getM_DDL() + job.rest_rtime*0.001
    # prio = job.getDDL()*1000 + job.rest_rtime*1
    return prio

def Add_processorpq(jobpq2,processor_pq):
    if(jobpq2.len() == 0):
        return processor_pq
    elif(jobpq2.len() == 1):
        j1 = jobpq2.pop()
        processor_pq.push(j1,j1.getDDL()*1000 + j1.rest_ctime)
    elif(jobpq2.len() == 2):
        j1 = jobpq2.pop()
        pri1 = j1.arrive_time
        processor_pq.push(j1,j1.getDDL()*1000 + j1.rest_ctime)
        j2 = jobpq2.pop()
        pri2 = j2.arrive_time
        if pri2 == pri1:
            processor_pq.push(j2,j2.getDDL()*1000 + j2.rest_ctime)
        else:
            jobpq2.push(j2,j2.arrive_time)
    else:
        j1 = jobpq2.pop()
        pri1 = j1.arrive_time
        processor_pq.push(j1,j1.getDDL()*1000 + j1.rest_ctime)
        j2 = jobpq2.pop()
        pri2 = j2.arrive_time
        flag = 1
        while(pri2 == pri1):
            flag = 0
            processor_pq.push(j2,j2.getDDL()*1000 + j2.rest_ctime)
            if jobpq2.len() >= 1:
                j2 = jobpq2.pop()
                pri2 = j2.arrive_time
                if pri2 != pri1:
                    flag = 1
            else:
                break
        if flag == 1:
            jobpq2.push(j2,j2.arrive_time)
    return processor_pq

def simulation(Pmax,jobpq1,channelpq,until,jobpq2,file_name):
    path = file_name + 'ins_act.log'
    f = open(path,'a')
    time_now = 0
    pro_status = 0
    last_status = pro_status
    upload_count = 0
    finish_count = 0
    drop_num1 = 0
    drop_num2 = 0
    CT = 0
    UCT = 0
    ACT = 0
    AUCT = 0
    #weight AUCT = \sum {(time_now - release_time)*(rest_ctime)/(ddl - release_time)}
    WAUCT = 0
    processor_pq = pq()
    while time_now <= until:
        time_driven = 0
        #如果processor is idle
        if pro_status == 0:
            #如果channelpq还有任务在队列中
            print("The processor is idle at ",time_now)
            if last_status == pro_status:
                channelpq = channelpq
            else:
                channelpq = SwitchPri(channelpq,pro_status,Pmax)
                last_status = pro_status
            if channelpq.len() >= 1:
                current_job = channelpq.pop()
                while(time_now + current_job.getRest_rtime() + current_job.getRest_ctime() > current_job.getDDL()):
                    current_job.drop = 1
                    print(current_job.name," is droped.")
                    drop_num1 += 1
                    if (channelpq.len() >= 1):
                        current_job = channelpq.pop()
                    else:
                        break
                #如果在当前时刻任务没有被drop，time_now++,如果当前的最后一个任务也被drop,时间不增加
                if current_job.drop == 0:
                    time_now += 1
                    current_job.Uploading()
                    time_driven = 1
                # current_job在time_now刚好上传完了
                if current_job.getRest_rtime() == 0:
                    UCT += time_now - current_job.release_time
                    WAUCT += (time_now -current_job.release_time)*current_job.rest_ctime/(time_now - current_job.release_time)
                    upload_count += 1
                # 如果job在上传截止时间前完成了
                    if time_now <= current_job.getDDL():
                        current_job.arrive_time = time_now 
                        pro_status = 1
                        jobpq2.push(current_job,current_job.arrive_time)
                    """ elif time_now > current_job.getDDL():
                        print("job",current_job.name,"miss deadline", file=f) """
                    # 检查是否有新的job被release
                    if (jobpq1.len() != 0):
                        job_temp = jobpq1.pop()
                        if job_temp.getRelease_time() == time_now:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                            Add_channelSRPT(jobpq1,channelpq,Pmax)
                        else: 
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                # current_job在time_now没有上传完
                else:
                    #print("len of jobpq1: ",jobpq1.len(), file=f)
                    if current_job.drop == 0: 
                        channelpq.push(current_job,SRPTprio(current_job,Pmax))
                    # 检查是否有新的job被release
                    if (jobpq1.len() != 0):
                        job_temp = jobpq1.pop()
                        if job_temp.getRelease_time() == time_now:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                            Add_channelSRPT(jobpq1,channelpq,Pmax)
                        else:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
            #在channel_pq没有任务的情况下
            else:
                time_now += 1
                time_driven = 1
                # 检查是否有新的job被release
                if (jobpq1.len() != 0):
                    job_temp = jobpq1.pop()
                    if job_temp.getRelease_time() == time_now:
                        jobpq1.push(job_temp,job_temp.getRelease_time())
                        Add_channelSRPT(jobpq1,channelpq,Pmax)
                    else: 
                        jobpq1.push(job_temp,job_temp.getRelease_time())
        #如果processor is busy
        else:
            print("The processor is busy at ",time_now)
            if last_status == pro_status:
                channelpq = channelpq
            else:
                channelpq = SwitchPri(channelpq,pro_status,Pmax)
                last_status = pro_status
            #如果channelpq还有任务在队列中
            if channelpq.len() >= 1:
                current_job = channelpq.pop()
                while(time_now + current_job.getRest_rtime() + current_job.getRest_ctime() > current_job.getDDL()):
                    print(current_job.name," is droped.")
                    current_job.drop = 1
                    drop_num1 += 1
                    if (channelpq.len() >= 1):
                        current_job = channelpq.pop()
                    else:
                        break
                #如果在当前时刻任务没有被drop，time_now++,如果当前的最后一个任务也被drop,时间不增加
                if current_job.drop == 0:
                    time_now += 1
                    current_job.Uploading()
                    time_driven = 1
                # current_job在time_now刚好上传完了
                if current_job.getRest_rtime() == 0:
                    #print("len of jobpq1: ",jobpq1.len(), file=f)
                    UCT += time_now - current_job.release_time
                    WAUCT += (time_now -current_job.release_time)*current_job.rest_ctime/(time_now - current_job.release_time)
                    upload_count += 1
                # 如果job在上传截止时间前完成了
                    if time_now <= current_job.getDDL():
                        current_job.arrive_time = time_now 
                        pro_status = 1
                        # print(current_job.name," ","time to arrive at the processor: ",current_job.arrive_time, file=f)
                        jobpq2.push(current_job,current_job.arrive_time)
                    """ elif time_now > current_job.getDDL():
                        print("job",current_job.name,"miss deadline", file=f) """
                    # 检查是否有新的job被release
                    if (jobpq1.len() != 0):
                        job_temp = jobpq1.pop()
                        if job_temp.getRelease_time() == time_now:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                            Add_channelEDF(jobpq1,channelpq,Pmax)
                        else: 
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                # current_job在time_now没有上传完
                else:
                    #print("len of jobpq1: ",jobpq1.len(), file=f)
                    if current_job.drop == 0:
                        channelpq.push(current_job,EDFprio(current_job,Pmax))
                    # 检查是否有新的job被release
                    if (jobpq1.len() != 0):
                        job_temp = jobpq1.pop()
                        if job_temp.getRelease_time() == time_now:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
                            Add_channelEDF(jobpq1,channelpq,Pmax)
                        else:
                            jobpq1.push(job_temp,job_temp.getRelease_time())
            #在channel_pq没有任务的情况下
            else:
                time_now += 1
                time_driven = 1
                # 检查是否有新的job被release
                if (jobpq1.len() != 0):
                    job_temp = jobpq1.pop()
                    if job_temp.getRelease_time() == time_now:
                        jobpq1.push(job_temp,job_temp.getRelease_time())
                        Add_channelEDF(jobpq1,channelpq,Pmax)
                    else: 
                        jobpq1.push(job_temp,job_temp.getRelease_time())
        #如果时间已经被驱动了一个slot,检查jobpq2和processorpq

        if time_driven == 1:
            print("len of processorpq: ",processor_pq.len())
            if (processor_pq.len() >= 1):
                pro_status = 1
                current_job = processor_pq.pop()
                while(time_now + current_job.getRest_ctime() - 1 > current_job.getDDL()):
                    current_job.drop = 1
                    drop_num2 += 1
                    if (processor_pq.len() >= 1):
                        current_job = processor_pq.pop()
                    else:
                        break
                if current_job.drop == 0:
                    current_job.Computing()
                    # print(current_job.name," is computing and the rest time is: ",current_job.rest_ctime," time is :",time_now,file=f)
                # current_job在time_now刚好做完了
                if current_job.getRest_ctime() == 0:
                    #print("len of jobpq2: ",jobpq2.len(),file=f)
                    # 如果job在计算截止时间前完成了
                    if time_now <= current_job.getDDL():
                        finish_count += 1
                        CT += time_now - current_job.release_time
                    elif time_now > current_job.getDDL():
                        print("job",current_job.name,"miss deadline",file=f)
                    # 检查是否有新的job arrive
                    if (jobpq2.len() != 0):
                        job_temp = jobpq2.pop()
                        if job_temp.arrive_time == time_now:
                            jobpq2.push(job_temp,job_temp.arrive_time)
                            Add_processorpq(jobpq2,processor_pq)
                        else: 
                            jobpq2.push(job_temp,job_temp.arrive_time)
                # current_job在time_now没有做完
                else:
                    pro_status = 1
                    #print("len of jobpq2: ",jobpq2.len(),file=f)
                    if current_job.drop == 0:
                        processor_pq.push(current_job,current_job.getDDL()*1000 + current_job.rest_ctime)
                    # 检查是否有新的job被release
                    if (jobpq2.len() != 0):
                        job_temp = jobpq2.pop()
                        if job_temp.arrive_time == time_now:
                            jobpq2.push(job_temp,job_temp.arrive_time)
                            Add_processorpq(jobpq2, processor_pq)
                        else:
                            jobpq2.push(job_temp,job_temp.arrive_time)
            else:
                # 检查是否有新的job被release
                if (jobpq2.len() >= 1):
                    job_temp = jobpq2.pop()
                    if job_temp.arrive_time == time_now:
                        jobpq2.push(job_temp,job_temp.arrive_time)
                        Add_processorpq(jobpq2,processor_pq)
                        pro_status = 1
                    else: 
                        jobpq2.push(job_temp,job_temp.arrive_time)
                        pro_status = 0
                else:
                    pro_status = 0
    AUCT = UCT/upload_count
    WAUCT = WAUCT/upload_count
    ACT = CT/finish_count
    print ("Number of tasks dropped during uploading: ",drop_num1,file=f)
    print ("Number of tasks dropped during computing: ",drop_num2,file=f)
    return finish_count, ACT, upload_count,AUCT

def MidSimuAnnACT(task_set,until,rt,file_name):
    path = file_name + 'ins_act.log'
    f = open(path,'w')
    print("This is a mid simu anneal simulation to get mid ddl sets",file=f)
    # print("release time is i*",rt,file=f)
    Pmax = GetMaxP(task_set,file_name)
    joblist = Ini_joblist(task_set,until,rt,file_name)
    print(len(joblist),file=f)
    jobpq1 = Ini_jobpq(joblist)
    jobpq2 = pq()   
    num_job = jobpq1.len()
    print("The number of jobs: ",num_job, file=f)
    channelpq = Ini_channelpq(jobpq1,Pmax)
    finish_count,ACT,upload_count,AUCT = simulation(Pmax,jobpq1,channelpq,until,jobpq2,file_name)
    if (finish_count == num_job):
        print("finish all jobs!!!", file=f)
        print("Number of jobs completed: ",finish_count,file=f)
        print("Number of jobs that miss deadline: ", num_job-finish_count,file=f)
        print("Average completion time: ", ACT, file=f)
    else:
        print("this task set is not feasible", file=f)
        print("Number of jobs completed: ",finish_count,file=f)
        print("Number of jobs that miss deadline: ", num_job-finish_count,file=f)
        print("Average completion time: ", ACT, file=f)
    # f.close()
    return finish_count,upload_count,num_job,AUCT



